﻿/****************************** Module Header ******************************\
* Module Name:    CustomTreeView.cs
* Project:        CSASPNETInheritingFromTreeNode
* Copyright (c) Microsoft Corporation
*
* This file defines a CustomTreeView control which's tree nodes contain a Tag 
* property. The Tag property can be used to store a custom object.
* 
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
\*****************************************************************************/

using System;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Web.UI.WebControls;

namespace CustomControls
{
    public class CustomTreeView : TreeView
    {
        public event EventHandler SelectedNodeClicked;
        private static readonly object SelectedNodeClickEvent;
        private const string CurrentValuePathState = "CurrentValuePath";

        protected string CurrentValuePath
        {
            get 
            {
                if (this.ViewState[CurrentValuePathState] != null)
                    return this.ViewState[CurrentValuePathState].ToString();
                else
                    return null;
            }
            set 
            { 
                this.ViewState[CurrentValuePathState] = value; 
            }
        }


        internal void RaiseSelectedNodeClicked()
        {
            this.OnSelectedNodeClicked(EventArgs.Empty);
        }

        protected virtual void OnSelectedNodeClicked(EventArgs e)
        {
            if (SelectedNodeClicked != null)
	            SelectedNodeClicked(this, e);
        }

        protected override void OnSelectedNodeChanged(System.EventArgs e)
        {
            base.OnSelectedNodeChanged(e);
            // Whenever the Selected Node changed, remember its ValuePath for future reference
            this.CurrentValuePath = this.SelectedNode.ValuePath;
        }

        protected override void RaisePostBackEvent(string eventArgument)
        {
            // Check if the node that caused the event is the same as the previously selected node
            if (this.SelectedNode != null && this.SelectedNode.ValuePath.Equals(this.CurrentValuePath))
            {
                this.RaiseSelectedNodeClicked();
            }

            base.RaisePostBackEvent(eventArgument);
        }

        /// <summary>
        /// Returns a new instance of the TreeNode class. The CreateNode is a helper method.
        /// </summary>
        protected override TreeNode CreateNode()
        {
            return new CustomTreeNode(this, false);
        }

    }
    [Serializable]
    public class CustomTreeNode : TreeNode , ISerializable
    {
        // A method called when serializing a Singleton.
        [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(
            SerializationInfo info, StreamingContext context)
        {
            // Instead of serializing this object, 
            // serialize a SingletonSerializationHelp instead.
            info.SetType(typeof(CustomTreeNodeHelper));
            info.AddValue("Tag", Tag, Tag.GetType());
            // No other values need to be added.
        }

            // The special constructor is used to deserialize values.
        public CustomTreeNode(SerializationInfo info, StreamingContext context)
        {
            // Reset the property value using the GetValue method.
            Tag = (object)info.GetValue("Tag", typeof(object));
        }

        // A method returning a reference to the Singleton.
        public static CustomTreeNode GetSingleton()
        {
            return theOneObject;
        }


        private static readonly CustomTreeNode theOneObject = new CustomTreeNode();

        /// <summary>
        /// Gets or sets the object that contains data about the tree node.
        /// </summary>
        public object Tag { get; set; }

        //private int index;
        ////public int Index
        //{
        //    get
        //    {
        //        return index;
        //    }
        //    set
        //    {
        //        this.index = value;
        //    }
        //}

        public CustomTreeNode()
            : base()
        {
        }

        public CustomTreeNode(string text)
            : base(text)
        {
        }

        public CustomTreeNode(string text, string imageUrl)
            : base(text)
        {
            base.ImageUrl = imageUrl;
        }

        public CustomTreeNode(TreeView owner, bool isRoot)
            : base(owner, isRoot)
        {
        }

        /// <summary>
        /// Restores view-state information from a previous page request that 
        /// was saved by the SaveViewState method.
        /// </summary>
        /// <param name="state">
        /// An Object that represents the control state to be restored. 
        /// </param>
        protected override void LoadViewState(object state)
        {
            object[] arrState = state as object[];

            this.Tag = arrState[0];
            base.LoadViewState(arrState[1]);
        }

        /// <summary>
        /// Saves any server control view-state changes that have occurred 
        /// since the time the page was posted back to the server.
        /// </summary>
        /// <returns>
        /// Returns the server control's current view state. If there is no 
        /// view state associated with the control, this method returns null.
        /// </returns>
        protected override object SaveViewState()
        {
            object[] arrState = new object[2];
            arrState[1] = base.SaveViewState();
            arrState[0] = this.Tag;

            return arrState;
        }
    }

    [Serializable]
    internal sealed class CustomTreeNodeHelper : IObjectReference
    {
        // This object has no fields (although it could).

        // GetRealObject is called after this object is deserialized.
        public object GetRealObject(StreamingContext context)
        {
            // When deserialiing this object, return a reference to 
            // the Singleton object instead.
            return CustomTreeNode.GetSingleton();
        }
    }
}
